/* 
 * File:   server.cpp
 * Author: Faraj
 * 
 * Created on May 11, 2013, 2:41 PM
 */
#include "web_server.h"
/*
 * initialize static
 */
vector<web::web_server> web::web_server::_servers;
const string web::web_server::DEFAULT_WEBROOT = "www";
/*
 * construct the web server
 */
web::web_server::web_server() {        
    lib::common::log::trace("web::web_server.web_server");
}
/*
 * destruct the web server
 */
web::web_server::~web_server() {    
    lib::common::log::trace("web::web_server.~web_server");
    for(int pos = 0; pos < web::web_server::_servers.size(); pos++){
        if(web::web_server::_servers.at(pos).get_port() == this->get_port()){
            web::web_server::_servers.erase(web::web_server::_servers.begin() + pos);
            break;
        }
    }
    this->~server();
}
/*
 * start the web server
 */
void web::web_server::start() {
    lib::common::log::trace("web::web_server.start");
    set_perform(_response);
    lib::server::start();
    if(is_running()){
        web::web_server::_servers.push_back(*this);
        lib::common::log::info("web server listening on port " + lib::common::str::to_str(get_port()));
    }else{
        lib::common::log::error("web::web_server.start failed on port " + lib::common::str::to_str(get_port()));
    }
}
/*
 * configure the web server by intaking config object
 */
void web::web_server::conf(lib::config* conf, string root_key){
    lib::common::log::trace("web::web_server.conf " + root_key);
    set_port(lib::common::str::to_int(conf->lookup(root_key + ".port")));
    set_home(conf->lookup(root_key + ".home"));
    conf_root(conf, root_key + ".root");
}
/*
 * set the home file values
 */
void web::web_server::set_home(string home_files){
    _home_files = lib::common::str::explode(",", home_files);
}
/*
 * configure the root folders
 */
void web::web_server::conf_root(lib::config *conf, string root_key) {
    lib::common::log::trace("web::web_server.conf_root " + root_key);
    vector<string> keys = conf->keys(root_key);
    for (std::vector<string>::iterator ikey = keys.begin() ; ikey != keys.end(); ++ikey){
        string path = conf->lookup(root_key + "." + *ikey);
        this->add_root(*ikey, path);
    }
}
/*
 * add a root folder
 */
void web::web_server::add_root(string name, string path) {
    lib::common::log::trace("web_server.add_root " + name);
    _roots[name] = path;
}
/*
 * web request response provider
 */
int web::web_server::_response(void *cls, struct MHD_Connection *connection,
        const char *url_s, const char *method,
        const char *version, const char *upload_data,
        size_t *upload_data_size, void **con_cls) {
    lib::common::log::trace("web_server._response");
    lib::url url_obj(lib::common::str::trim(string(url_s)));    
    const MHD_ConnectionInfo *conn_info = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_DAEMON);    
    lib::common::log::info("web request method: " + string(method) + ", version: " + string(version) + ", url:" + string(url_s));
    for(int pos = 0; pos < web::web_server::_servers.size(); pos++){
        if(web::web_server::_servers.at(pos).get_daemon() == conn_info->daemon){
            web::web_server *wserver;            
            wserver = &web::web_server::_servers.at(pos);
            string base_path = "."+ lib::common::os::dir_seperator() + web::web_server::DEFAULT_WEBROOT;
            if(wserver->_roots.find(url_obj.get_base())!= wserver->_roots.end()){
                base_path = wserver->_roots.at(url_obj.get_base());
            }            
            string resource_path =  base_path  + lib::common::os::dir_seperator() + url_obj.get_file_path();            
            lib::resource *res_file = new lib::resource(resource_path);
            lib::common::log::trace("web_server._response.res_open");
            res_file->open();            
            struct MHD_Response *response;
            int ret_response;
            lib::common::log::trace("web_server._response.check_valid_file");
            if(res_file->is_valid_folder()){
                lib::common::log::trace("web_server._response.check_home_files");    
                for(vector<string>::iterator homeit = wserver->_home_files.begin(); homeit != wserver->_home_files.end(); ++homeit){
                    res_file->~resource();
                    res_file = new lib::resource(resource_path + lib::common::os::dir_seperator() + *homeit);
                    res_file->open();
                    if(res_file->is_valid()){
                        break;
                    }
                }
            }
            if(res_file->is_valid()){                
                lib::common::log::trace("web_server._response.is_data_file");
                if(res_file->is_data_file()){
                    lib::common::log::trace("web_server._response.data_file");    
                    res_file->close();
                    response = MHD_create_response_from_fd(res_file->get_size(), (int)res_file->get_fd());                                                    
                }else{
                    lib::common::log::trace("web_server._response.application_execute");
                    res_file->execute();
                    response = MHD_create_response_from_buffer(res_file->get_size(), (void *) res_file->get_content(), MHD_RESPMEM_PERSISTENT);                                                    
                    res_file->close();
                }
                ret_response = MHD_queue_response(connection, MHD_HTTP_OK, response);                
            }else{
                lib::common::log::warn("web_server._response.HTTP_NOT_FOUND");      
                response = MHD_create_response_from_buffer(0, (void *) "<html><head><title>File not found</title></head><body>File not found</body></html>", MHD_RESPMEM_PERSISTENT);
                ret_response = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);                
            }
            MHD_destroy_response(response);            
            return ret_response;            
        }
    }
}